#ifndef AGT_MODEL_H
#define AGT_MODEL_H

#define AE_LATEST_MODEL_VERSION 1
#define AE_LATEST_MODEL_REVISION 1

namespace agtk {

/** Struct for storing vertex position, normal and texture coordinates.
	@author Tom Savage
**/
struct Vertex {
	/** Vertex position. **/
	Vec3<float>* m_pos;

	/** Vertex normal. **/
	Vec3<float>* m_normal;

	/** Vertex texture coordinates 0.0~1.0. **/
	Vec2<float>* m_texCoords;

	/** Vertex tangent. **/
	Vec3<float>* m_tangent;

	/** Vertex bitangent. **/
	Vec3<float>* m_bitangent;

	/** Vertex constructor.
		@param posx x coordinate.
		@param posy y coordinate.
		@param posz z coordinate.
		@param normx x normal vector part.
		@param normy y normal vector part.
		@param normz z normal vector part.
		@param texturex x tecture coordinate (U).
		@param texturey y tecture coordinate (V).
	**/
	Vertex(float posx, float posy, float posz, float normx, float normy, float normz, float texturex, float texturey) {
		m_pos = new Vec3<float>(posx, posy, posz);
		m_normal = new Vec3<float>(normx, normy, normz);
		m_texCoords = new Vec2<float>(texturex, texturey);
		m_tangent = new Vec3<float>;
		m_bitangent = new Vec3<float>;
	}
};

/** Struct containing a linked list of vertex objects.
	@author Tom Savage
**/
struct Triangle {
	Vertex* m_vertices[3]; // Array of vertices in this triangle
	~Triangle () {
		delete[]&m_vertices;
	}


	void CalculateTangentSpace(void) {
		float x1 = m_vertices[1]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0);
		float x2 = m_vertices[2]->m_pos->Get(0) - m_vertices[0]->m_pos->Get(0);
		float y1 = m_vertices[1]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1);
		float y2 = m_vertices[2]->m_pos->Get(1) - m_vertices[0]->m_pos->Get(1);
		float z1 = m_vertices[1]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2);
		float z2 = m_vertices[2]->m_pos->Get(2) - m_vertices[0]->m_pos->Get(2);

		float u1 = m_vertices[1]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0);
		float u2 = m_vertices[2]->m_texCoords->Get(0) - m_vertices[0]->m_texCoords->Get(0);
		float v1 = m_vertices[1]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1);
		float v2 = m_vertices[2]->m_texCoords->Get(1) - m_vertices[0]->m_texCoords->Get(1);

		float r = 1.0f/(u1 * v2 - u2 * v1);

		Vec3<float> udir((v2 * x1 - v1 * x2) * r, (v2 * y1 - v1 * y2) * r, (v2 * z1 - v1 * z2) * r);
		Vec3<float> vdir((u1 * x2 - u2 * x1) * r, (u1 * y2 - u2 * y1) * r, (u1 * z2 - u2 * z1) * r);
		
		Vec3<float> tangent[3];
		Vec3<float> tempNormal;

		tempNormal = *m_vertices[0]->m_normal;
		tangent[0]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
		m_vertices[0]->m_tangent=&(tangent[0].Normalize());
		m_vertices[0]->m_bitangent=Vec3Cross(m_vertices[0]->m_normal, m_vertices[0]->m_tangent);

		tempNormal = *m_vertices[1]->m_normal;
		tangent[1]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
		m_vertices[1]->m_tangent=&(tangent[1].Normalize());
		m_vertices[1]->m_bitangent=Vec3Cross(m_vertices[1]->m_normal, m_vertices[1]->m_tangent);

		tempNormal = *m_vertices[2]->m_normal;
		tangent[2]=(udir-tempNormal*(Vec3Dot(tempNormal, udir)));
		m_vertices[2]->m_tangent=&(tangent[2].Normalize());
		m_vertices[2]->m_bitangent=Vec3Cross(m_vertices[2]->m_normal, m_vertices[2]->m_tangent);
	}
};

/** ModelVersion structure. The first thing writen and read. Must not change to keep backwards compatibility. **/
struct ModelVersion
{
	int version;
	int revision;
};

/** ModelHeader structure. Contains information about the model. **/
struct ModelHeader
{
	/** A name for the model like 'spaceship'. **/
	std::string title;
	
	/** A description of the model. **/
	std::string description;
	
	/** Who made the model? **/
	std::string author;
	
	/** The file name of the default material that the model uses (e.g. myMaterial.material). **/
	std::string defaultMaterialName;
	
	/** The path to the containing folder of the default material (e.g. ./Assets/Materials/) Must have trailing slash "/"! **/
	std::string defaultMaterialPath;
	
	/** The number of triangles in the model. **/
	unsigned int triangleCount;
	
	ModelHeader(void) {
		title = "";
		description = "";
		author = "";
		defaultMaterialName = "";
		defaultMaterialPath = "";
		triangleCount = 0;
	}
};

/** Model Class. Inherits from Resource<Model>.
	@author Tom Savage
**/
class Model : public Resource<Model>
{
public:
	/** Model constructor.
		@param name File name of the requested model file.
		@param path Path to specified file relative to executing directory. Must include trailing slash "/". Defaults to "./".
	**/
	Model(char* name, char* path); // Must not be changed for resource manager compatibility

	/** Model destructor. **/
	~Model(void);

	/** Load function. Called from constructor.
		@return bool. True on sucess.
	**/
	bool Load();

	/** Render function. Draws model to the screen. **/
	void Render();

	/** Render with a given material.
		@param Material* Pointer to the material to use.
	**/
	void Render(Material* material);

	/** Return a pointer to the current material. **/
	Material* GetMaterial();

private:
	/** The version data for the model file. **/
	ModelVersion m_version;

	/** The header data for the model file. **/
	ModelHeader m_header;

	/** File name of the model (e.g. myModel.model). **/
	char* m_czName;

	/** The path to the containing folder of the model (e.g. ./Assets/Models/) Must have trailing "/"! **/
	char* m_czPath;

	/** An automatically generated combination of the file name and path (e.g. ./Assets/Models/myModel.model). **/
	char* m_czFullPath;

	/** Linked list of Triangle objects in the model. **/
	LinkedList<Triangle> m_triangles;

	/** Pointer to active material (in material manager). **/
	Material* m_currentMaterial;

	/** Is true when model is fully loaded. Ensures model is not rendered until it is safe to do so. **/
	bool m_bLoaded;
};

}

#endif